namespace Nomo.CoreModule
{
    /// <summary>
    /// Manage the subscription, unsubscription, and invocation of events associated with and distinguished by ID.
    /// </summary>
    public static class Event
    {
        private static readonly System.Collections.Generic.Dictionary<int, System.Delegate> Delegates = new System.Collections.Generic.Dictionary<int, System.Delegate>();

        /// <summary>
        /// Subscribes to an event.
        /// </summary>
        /// <param name="id">An ID that the event associated with.</param>
        /// <param name="delegate">A delegate that used to subscribe to the event associated with <paramref name="id"/>.</param>
        /// <exception cref="System.ArgumentException">Both <paramref name="delegate"/> and the event associated with <paramref name="id"/> are not <see langword="null"/>, and they are not instances of the same delegate type.</exception>
        public static void Subscribe(int id, System.Delegate @delegate)
        {
            if (@delegate == null)
            {
                return;
            }
            if (Delegates.TryGetValue(id, out var value))
            {
                @delegate = System.Delegate.Combine(value, @delegate);
            }
            Delegates[id] = @delegate;
        }

        /// <summary>
        /// Unsubscribes from an event.
        /// </summary>
        /// <param name="id">An ID that the event associated with.</param>
        /// <param name="delegate">A delegate that used to unsubscribe from the event associated with <paramref name="id"/>.</param>
        /// <exception cref="System.ArgumentException">Both <paramref name="delegate"/> and the event associated with <paramref name="id"/> are not <see langword="null"/>, and they are not instances of the same delegate type.</exception>
        public static void Unsubscribe(int id, System.Delegate @delegate)
        {
            if (@delegate == null)
            {
                return;
            }
            if (!Delegates.TryGetValue(id, out var value))
            {
                return;
            }
            if ((@delegate = System.Delegate.Remove(value, @delegate)) != null)
            {
                Delegates[id] = @delegate;
            }
            else
            {
                Delegates.Remove(id);
            }
        }

        /// <summary>
        /// Raises an event.
        /// </summary>
        /// <param name="id">An ID that the event associated with.</param>
        /// <param name="args">An array of objects that are the arguments to pass to the event associated with <paramref name="id"/>.</param>
        /// <exception cref="System.Reflection.TargetParameterCountException">The number of parameters listed in <paramref name="args"/> is invalid.</exception>
        /// <exception cref="System.ArgumentException">The order, or type of parameters listed in <paramref name="args"/> is invalid.</exception>
        public static void Invoke(int id, params object[] args)
        {
            if (!Delegates.TryGetValue(id, out var value))
            {
                return;
            }
            value.DynamicInvoke(args);
        }

        /// <summary>
        /// Removes all events and IDs associated with them from the <see cref="Event"/>.
        /// </summary>
        public static void Clear()
        {
            Delegates.Clear();
        }
    }
}
